/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.google.cse.emea.universaltransformer;

import com.google.cse.emea.jct.utils.Profile;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import org.joda.time.DateTime;
import static java.util.logging.Level.*;
import static com.google.cse.emea.universaltransformer.TaskList.*;
import static com.google.cse.emea.universaltransformer.TransformLogger.*;

/**
 *
 * @author jtrapp
 */
class ParameterTableModel extends AbstractMap<String, String> implements javax.swing.table.TableModel {
    private ArrayList<TableModelListener> listeners = new ArrayList<TableModelListener>();
    private boolean[] canEdit = new boolean[]{false, true};
    private String[] columnNames = new String[]{"Parameter", "Value"};
    private ArrayList<String> keys = new ArrayList<String>();
    private ArrayList<String> labels = new ArrayList<String>();
    private ArrayList<String> values = new ArrayList<String>();

    public ParameterTableModel(Profile profile) {
        setProfile(profile);
    }
    
    public void setProfile(Profile profile){
        int numcolumns = 0;
        try {
            numcolumns = Integer.parseInt(profile.get(TransformProfileEnum.TRANSFORM_REQUIRE_PARAMS));
        } catch (NumberFormatException nfe) {
        //ignore table
        //todo clear table
        }

        if (numcolumns < 0) {
            numcolumns = 0;
        } else if (numcolumns > 100) {
            log(SEVERE, "Number of columns out of range.");
            numcolumns = 10;
        }
        
        for (int i = 0; i < numcolumns; i++) {
            String label = profile.get(TransformProfileEnum.TRANSFORM_PARAMS, i, ".label");
            String key = profile.get(TransformProfileEnum.TRANSFORM_PARAMS, i, ".name");
            String valueString = profile.get(TransformProfileEnum.TRANSFORM_PARAMS, i, ".value");
            String value = calcValue(valueString, null);
            keys.add(key);
            labels.add(label);
            values.add(value);
        }
    }


    public Class getColumnClass(int columnIndex) {
        return String.class;
    }

    public boolean isCellEditable(int rowIndex, int columnIndex) {
        return canEdit[columnIndex];
    }

    /** calc a value using the javascript engine. Used for evaluation
     * of dynamic parameters.*/
    private String calcValue(String line, String[] args) {
        //return Boolean.parseBoolean(condition);
        ScriptEngineManager mgr = new ScriptEngineManager();
        ScriptEngine jsEngine = mgr.getEngineByName("JavaScript");
        Invocable jsInvoke = (Invocable) jsEngine;
        try {
            jsEngine.put("datetime", new DateTime());
            if (args != null) {
                for (int i = 0; i < args.length; i++) {
                    jsEngine.put("a" + i, args[i]);
                }
            }
            jsEngine.eval("function run() { return " + line + "; }");
            Object value = jsInvoke.invokeFunction("run");
            if (value != null) {
                log(FINER, "Value eval:" + value);
                return value.toString();
            } else {
                log(WARNING, "Null value while evaluating condition " + line + ".");
                return "";
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            log(SEVERE, "Error while evaluating condition " + line + " . ");
            return "";
        }
    }

//    {
//        int numcolumns = 0;
//            try {
//                numcolumns = Integer.parseInt(KEY_TRANSFORM_REQUIRE_PARAMS.getProperty());
//            } catch (NumberFormatException nfe) {
//            //ignore table
//            }
//            if (numcolumns > 0) {
//
//                for (int i = 0; i < numcolumns; i++) {
//                    String name = KEY_TRANSFORM_PARAMS.getProperty(i, ".name");
//                    String value = parameterTable.getModel().getValueAt(i, 1).toString();
//                    if (KEY_TRANSFORM_PARAMS.getBoolean(i, ".remember")) {
//                        KEY_TRANSFORM_PARAMS.setProperty(i, ".value", value);
//                    }
//                }
//            }
//    
//}
    @Override
    public Set<Entry<String, String>> entrySet() {
        return new Set<Entry<String, String>>() {

            public int size() {
                return keys.size();
            }

            public boolean isEmpty() {
                return keys.isEmpty();
            }

            public boolean contains(Object o) {
                return keys.contains(o);
            }

            public Iterator<Entry<String, String>> iterator() {
                return new Iterator<Entry<String, String>>() {

                    int index = 0;

                    public boolean hasNext() {
                        return index < keys.size();
                    }

                    public Entry<String, String> next() {
                        index++;
                        return new Entry<String, String>() {

                            public String getKey() {
                                return (String) keys.get(index-1);
                            }

                            public String getValue() {
                                return (String) values.get(index-1);
                            }

                            public String setValue(String value) {
                                String returnValue = values.set(index-1, value);
                                fireTableChangedEvent(index-1);
                                return returnValue;
                            }
                        };

                    }

                    public void remove() {
                        throw new UnsupportedOperationException("Not supported yet.");
                    }
                };
            }

            public Object[] toArray() {
                return values.toArray();
            }

            public <T> T[] toArray(T[] a) {
                return values.toArray(a);
            }

            public boolean add(Entry<String, String> e) {
                int index = keys.indexOf(e.getKey());
                if (index == -1) {
                    if (keys.add(e.getKey())) {
                        boolean retValue = values.add(e.getValue());
                        fireTableChangedEvent(keys.size() - 1);
                        return retValue;
                    } else {
                        return false;
                    }
                } else {
                    values.set(index, e.getValue());
                    fireTableChangedEvent(index);
                    return true;
                }
            }

            public boolean remove(Object o) {
                if (o instanceof String) {
                    int index = keys.indexOf(o);

                    if (index >= 0) {
                        keys.remove(index);
                        values.remove(index);
                        fireTableChangedEvent();
                        return true;
                    }
                }
                return false;
            }

            public boolean containsAll(Collection<?> c) {
                throw new UnsupportedOperationException("Not supported yet.");
            }

            public boolean addAll(Collection<? extends Entry<String, String>> c) {
                for (Entry<String, String> e : c) {
                    keys.add(e.getKey());
                    values.add(e.getValue());
                }
                fireTableChangedEvent();
                return true;
            }

            public boolean retainAll(Collection<?> c) {
                throw new UnsupportedOperationException("Not supported yet.");
            }

            public boolean removeAll(Collection<?> c) {
                throw new UnsupportedOperationException("Not supported yet.");
            }

            public void clear() {
                keys.clear();
                values.clear();
                labels.clear();
                fireTableChangedEvent();
            }
        };
    }

    public int getRowCount() {
        return keys.size();
    }

    public int getColumnCount() {
        return columnNames.length;
    }

    public String getColumnName(int columnIndex) {
        return columnNames[columnIndex];
    }

    public Object getValueAt(int rowIndex, int columnIndex) {
        switch (columnIndex) {
            case 0:
                return labels.get(rowIndex);
            case 1:
                return values.get(rowIndex);
            default:
                throw new IndexOutOfBoundsException("Only two columns available " + columnIndex);
        }
    }

    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        switch (columnIndex) {
            case 0:
                labels.set(rowIndex, aValue.toString());
                fireTableChangedEvent(rowIndex);
                return;
            case 1:
                values.set(rowIndex, aValue.toString());
                fireTableChangedEvent(rowIndex);
                return;
            default:
                throw new IndexOutOfBoundsException("Only two columns available " + columnIndex);
        }
    }

    private void fireTableChangedEvent() {
        for (TableModelListener l : listeners) {
            l.tableChanged(new TableModelEvent(this));
        }
    }

    private void fireTableChangedEvent(int row) {
        for (TableModelListener l : listeners) {
            l.tableChanged(new TableModelEvent(this, row));
        }
    }


    public void addTableModelListener(TableModelListener l) {
        listeners.add(l);
    }

    public void removeTableModelListener(TableModelListener l) {
        listeners.remove(l);
    }
}
